home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 19 / Amiga Plus Leser CD 19.iso / Tools / Freeware / Swf_Player / Lib / displaylist.cc < prev    next >
Encoding:
C/C++ Source or Header  |  2002-11-17  |  18.0 KB  |  709 lines

  1. ////////////////////////////////////////////////////////////
  2. // Flash Plugin and Player
  3. // Copyright (C) 1998,1999 Olivier Debon
  4. // 
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU General Public License
  7. // as published by the Free Software Foundation; either version 2
  8. // of the License, or (at your option) any later version.
  9. // 
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. // GNU General Public License for more details.
  14. // 
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18. // 
  19. ///////////////////////////////////////////////////////////////
  20. //  Author : Olivier Debon  <odebon@club-internet.fr>
  21. //  
  22.  
  23. #include "swf.h"
  24.  
  25. #ifdef RCSID
  26. static char *rcsid = "$Id: displaylist.cc,v 1.4 1999/09/03 15:17:40 ode Exp $";
  27. #endif
  28.  
  29. #define PRINT 0
  30.  
  31. void deleteButton(FlashMovie *movie, DisplayListEntry *e)
  32. {
  33.     /* save the focus */
  34.     if (movie->mouse_active == 0 && e->renderState == stateOver) {
  35.         movie->lost_over = (Button *)e->character;
  36.         movie->cur_focus = NULL;
  37.     }
  38.  
  39.     if (e == movie->cur_focus) {
  40.         movie->cur_focus = NULL;
  41.     }
  42. }
  43.  
  44. void addButton(FlashMovie *movie, DisplayListEntry *e)
  45. {
  46.     if (movie->mouse_active == 0 && 
  47.         movie->cur_focus == NULL &&
  48.         movie->lost_over == (Button *)e->character) {
  49.         /* restore the lost focus */
  50.         e->renderState = stateOver;
  51.         e->oldState = stateOver;
  52.     ((Button *)e->character)->updateButtonState(e);
  53.         movie->lost_over = NULL;
  54.         movie->cur_focus = e;
  55.     }
  56. }
  57.  
  58. DisplayList::DisplayList(FlashMovie *movie)
  59. {
  60.     list = NULL;
  61.         this->movie = movie;
  62.     bbox.reset();
  63.     isSprite = 0;
  64. }
  65.  
  66. DisplayList::~DisplayList()
  67. {
  68.     clearList();
  69. }
  70.  
  71. void
  72. DisplayList::clearList()
  73. {
  74.     DisplayListEntry *del, *e;
  75.  
  76.     for(e = list; e;)
  77.     {
  78.         updateBoundingBox(e);
  79.                 if (e->character->isButton()) {
  80.                     deleteButton(movie,e);
  81.                 }
  82.         del = e;
  83.         e = e->next;
  84.         delete del;
  85.     }
  86.     list = 0;
  87. }
  88.  
  89. DisplayListEntry *
  90. DisplayList::getList()
  91. {
  92.     return list;
  93. }
  94.  
  95. static void bbox(Rect *rect, Matrix *m, long x1, long y1)
  96. {
  97.     long x,y;
  98.  
  99.     x = m->getX(x1,y1);
  100.     y = m->getY(x1,y1);
  101.     if (x < rect->xmin) rect->xmin = x;
  102.     if (x > rect->xmax) rect->xmax = x;
  103.     if (y < rect->ymin) rect->ymin = y;
  104.     if (y > rect->ymax) rect->ymax = y;
  105. }
  106.  
  107. // Update bb to include boundary, optional reset of bb
  108. void transformBoundingBox(Rect *bb, Matrix *matrix, Rect *boundary, int reset)
  109. {
  110.     if (reset) {
  111.         bb->reset();
  112.     }
  113.     
  114.     if (boundary->xmin != LONG_MAX) {
  115.         bbox(bb, matrix, boundary->xmin, boundary->ymin);
  116.         bbox(bb, matrix, boundary->xmax, boundary->ymin);
  117.         bbox(bb, matrix, boundary->xmin, boundary->ymax);
  118.         bbox(bb, matrix, boundary->xmax, boundary->ymax);
  119.     }
  120. }
  121.  
  122. void
  123. DisplayList::placeObject(GraphicDevice *gd,Character *character, long depth, Matrix *matrix, Cxform *cxform, char *name)
  124. {
  125.     DisplayListEntry *n,*e,*prev;
  126.  
  127.     n = new DisplayListEntry;
  128.     if (n == NULL) return;
  129.  
  130.     n->depth = depth;
  131.     n->matrix = matrix;
  132.     n->cxform = cxform;
  133.     n->character = character;
  134.     n->instanceName = name;
  135.     n->owner = this;
  136.  
  137. #if 0
  138.         printf("Dl %lx: placeObject: depth=%d character=%d cxform=%p\n",
  139.                this, n->depth,n->character ? n->character->getTagId() : 0, cxform);
  140. #endif
  141.  
  142.     if (character == 0 || matrix == 0 || cxform == 0) {
  143.         for (e = list; e; prev = e, e = e->next) {
  144.             if (e->depth == n->depth) {
  145.                 if (character == 0) {
  146.                     n->character = e->character;
  147.                 }
  148.                 if (matrix == 0) {
  149.                     n->matrix = e->matrix;
  150.                 }
  151.                 if (cxform == 0) {
  152.                     n->cxform = e->cxform;
  153.                 }
  154.                 break;
  155.             }
  156.         }
  157.     }
  158.  
  159.     if (n->character == 0) {
  160.         // Not found !!!    Should not happen
  161.             //        printf("PlaceObject cannot find character at depth %ld\n", n->depth);
  162.         delete n;
  163.         return;
  164.     }
  165.  
  166.     prev = 0;
  167.     for (e = list; e; prev = e, e = e->next)
  168.     {
  169.         if (e->depth == n->depth) {
  170.                         if (e->character->isButton()) {
  171.                             deleteButton(movie, e);
  172.                         }
  173.  
  174.             // Do update, object has moved or been resized
  175.                 updateBoundingBox(e);
  176.  
  177.             // Replace object
  178.                         e->depth = n->depth;
  179.                         e->matrix = n->matrix;
  180.                         e->cxform = n->cxform;
  181.                         e->character = n->character;
  182.                         /* if it is a button, we must update its state */
  183.                         if (e->character->isButton()) {
  184.                             movie->buttons_updated = 1;
  185.                             addButton(movie, e);
  186.                         }
  187.  
  188.                 updateBoundingBox(e);
  189.  
  190.                         delete n;
  191.                         return;
  192.         }
  193.         if (e->depth > n->depth) break;
  194.     }
  195.         /* new object */
  196.  
  197.         /* button instantiation */
  198.         if (n->character->isButton()) {
  199.             n->renderState = stateUp;
  200.             n->oldState = stateUp;
  201.         ((Button *)n->character)->updateButtonState(n);
  202.             addButton(movie,n);
  203.         }
  204.  
  205.     updateBoundingBox(n);
  206.  
  207.     if (prev == 0) {
  208.         // Object comes at first place
  209.         n->next = list;
  210.         list = n;
  211.     } else {
  212.         // Insert object
  213.         n->next = prev->next;
  214.         prev->next = n;
  215.     }
  216. }
  217.  
  218.  
  219. Character *
  220. DisplayList::removeObject(GraphicDevice *gd,Character *character, long depth)
  221. {
  222.     DisplayListEntry *e,*prev;
  223.     
  224.     // List should not be empty
  225.     if (list == 0) return 0;
  226.     
  227. #if 0
  228.     printf("removeObject: depth=%d character=%d\n",
  229.            depth,character ? character->getTagId() : 0);
  230. #endif
  231.     
  232.     prev = 0;
  233.     for (e = list; e; prev = e, e = e->next) {
  234.         if (e->depth == depth) {
  235.             if (prev) {
  236.                 prev->next = e->next;
  237.             } else {
  238.                 list = e->next;
  239.             }
  240.             if (character == 0) {
  241.                 character = e->character;
  242.             }
  243.             if (e->character->isButton()) {
  244.                 deleteButton(movie, e);
  245.             }
  246.             if (e->character->isSprite()) {
  247.             ((Sprite*)e->character)->reset();
  248.         }
  249.                 
  250.         updateBoundingBox(e);
  251.  
  252.             delete e;
  253.             return character;
  254.         }
  255.     }
  256.     return 0;    // Should not happen
  257. }
  258.  
  259. void
  260. DisplayList::updateBoundingBox(DisplayListEntry *e)
  261. {
  262.     Rect     rect;
  263.  
  264.     //rect.reset();
  265.     e->character->getBoundingBox(&rect,e);
  266.     transformBoundingBox(&this->bbox, e->matrix, &rect, 0);
  267. }
  268.  
  269. int
  270. DisplayList::updateSprites()
  271. {
  272.     Sprite *sprite;
  273.     DisplayListEntry *e;
  274.     int refresh = 0;
  275.  
  276.     for (e = this->list; e != NULL; e = e->next) {
  277.         if (e->character->isButton() && e->buttonCharacter) {
  278.         if (e->buttonCharacter->isSprite()) {
  279.             Matrix mat;
  280.  
  281.             sprite = (Sprite *)e->buttonCharacter;
  282.             refresh |= sprite->program->dl->updateSprites();
  283.             refresh |= sprite->program->nestedMovie(this->movie->gd,this->movie->sm, e->matrix, e->cxform);
  284.             mat = (*e->matrix) * e->buttonMatrix;
  285.             transformBoundingBox(&this->bbox, &mat,
  286.                     &(sprite->program->dl->bbox),
  287.                     0);
  288.         }
  289.     }
  290.         if (e->character->isSprite()) {
  291.         sprite = (Sprite *)e->character;
  292.         refresh |= sprite->program->dl->updateSprites();
  293.         refresh |= sprite->program->nestedMovie(this->movie->gd,this->movie->sm, e->matrix, e->cxform);
  294.         transformBoundingBox(&this->bbox, e->matrix,
  295.                 &(sprite->program->dl->bbox),
  296.                 0);
  297.         }
  298.     }
  299.     return refresh;
  300. }
  301.  
  302. /* Function can return either 0,1 or 2
  303.    0:  Nothing match, continue
  304.    1:  Something matches, but continue searching
  305.    2:  Something matches, but stop searching
  306. */
  307.  
  308. static int exploreButtons1(Program *prg, void *opaque, 
  309.                            ExploreButtonFunc func)
  310. {
  311.     DisplayListEntry *e;
  312.     int ret, ret2 = 0;
  313.  
  314.     for(e=prg->dl->list; e != NULL; e = e->next) {
  315.     if (e->character == NULL) continue;
  316.         if (e->character->isButton()) {
  317.             ret = func(opaque,prg,e);
  318.         if (ret == 2) return ret;    // Func asks to return at once !!!
  319.             if (ret) ret2 = 1;
  320.         }
  321.         if (e->character->isSprite()) {
  322.             ret = exploreButtons1(((Sprite *)e->character)->program,
  323.                                   opaque,func);
  324.         if (ret == 2) return ret;    // Func asks to return at once !!!
  325.             if (ret) ret2 = 1;
  326.         }
  327.     }
  328.     return ret2;
  329. }
  330.  
  331. int exploreButtons(FlashMovie *movie, void *opaque, ExploreButtonFunc func)
  332. {
  333.     CInputScript *script;
  334.     int ret;
  335.  
  336.     script = movie->main;
  337.     while (script != NULL) {
  338.     if (script->program) {
  339.         ret = exploreButtons1(script->program, opaque, func);
  340.         if (ret) return ret;
  341.     }
  342.         script = script->next;
  343.     }
  344.     return 0;
  345. }
  346.  
  347. typedef struct {
  348.     long x,y;
  349.     int hit;
  350.     DisplayListEntry *bhit;
  351. } HitTable;
  352.  
  353. static void button_hit_func(void *id, long y, long start, long end)
  354. {
  355.     HitTable *h = (HitTable *) id;
  356.     if ( y == h->y && (h->x >= start && h->x < end) )
  357.         h->hit = 1;
  358. }
  359.  
  360. typedef struct {
  361.     FlashMovie *movie;
  362.     DisplayListEntry *bhit;
  363. } ButtonHit;
  364.  
  365. static int button_hit(void *opaque, Program *prg, DisplayListEntry *e)
  366. {
  367.     ButtonHit *h = (ButtonHit *) opaque;
  368.     HitTable hit_table;
  369.     FlashMovie *movie = h->movie;
  370.     Rect bb,boundary;
  371.     Matrix mat;
  372.     ButtonState save;
  373.  
  374.     hit_table.x = movie->mouse_x;
  375.     hit_table.y = movie->mouse_y / FRAC;
  376.     hit_table.hit = 0;
  377.     
  378.     // Compute the bounding box in screen coordinates
  379.     save = e->renderState;
  380.     e->renderState = stateHitTest;
  381.     e->character->getBoundingBox(&boundary,e);
  382.     e->renderState = save;
  383.     mat = (*movie->gd->adjust) * e->renderMatrix;
  384.     transformBoundingBox(&bb, &mat, &boundary, 1);
  385.     // Check if mouse is within bb
  386.     if (movie->mouse_x < bb.xmin) return 0;
  387.     if (movie->mouse_x > bb.xmax) return 0;
  388.     if (movie->mouse_y < bb.ymin) return 0;
  389.     if (movie->mouse_y > bb.ymax) return 0;
  390.  
  391.     e->character->getRegion(movie->gd, &e->renderMatrix, 
  392.                             &hit_table, button_hit_func);
  393.                 
  394.     if (hit_table.hit) {
  395.         h->bhit = e;
  396.         return 1;
  397.     } else {
  398.         return 0;
  399.     }
  400. }
  401.  
  402. static int button_reset(void *opaque, Program *prg, DisplayListEntry *e)
  403. {
  404.     if (e->renderState != stateUp) {
  405.         e->owner->updateBoundingBox(e);
  406.         e->oldState = e->renderState;
  407.         e->renderState = stateUp;
  408.         ((Button *)e->character)->updateButtonState(e);
  409.         e->owner->updateBoundingBox(e);
  410.     }
  411.     return 0;
  412. }
  413.  
  414. /* update the button states according to the current mouse state & return the list of actions */
  415. void
  416. DisplayList::updateButtons(FlashMovie *movie)
  417. {
  418.     DisplayListEntry *bhit;
  419.     ButtonHit h;
  420.  
  421.     if (movie->mouse_active) {
  422.  
  423.         h.bhit = NULL;
  424.         h.movie = movie;
  425.  
  426.         exploreButtons(movie, &h, button_hit);
  427.  
  428.         bhit = h.bhit;
  429.  
  430.         /* set every button to not hit */
  431.         exploreButtons(movie, NULL, button_reset);
  432.  
  433.         if (bhit) {
  434.         ButtonState state;
  435.  
  436.             if (movie->button_pressed) {
  437.                 state = stateDown;
  438.             } else {
  439.                 state = stateOver;
  440.             }
  441.         if (state != bhit->renderState) {
  442.             bhit->owner->updateBoundingBox(bhit);
  443.             bhit->renderState = state;
  444.             ((Button *)bhit->character)->updateButtonState(bhit);
  445.             bhit->owner->updateBoundingBox(bhit);
  446.             movie->cur_focus = bhit;
  447.             if (movie->cursorOnOff)
  448.                 movie->cursorOnOff(1,movie->cursorOnOffClientData);
  449.         }
  450.         } else {
  451.         if (movie->cursorOnOff)
  452.             movie->cursorOnOff(0,movie->cursorOnOffClientData);
  453.     }
  454.     }
  455. }
  456.  
  457. typedef struct {
  458.     ActionRecord *action;    // Action to do
  459.     Program     *prg;        // Context program
  460. } ButtonAction;
  461.  
  462. static int button_action(void *opaque, Program *prg, DisplayListEntry *e)
  463. {
  464.     ButtonAction *h = (ButtonAction *)opaque;
  465.     static ActionRecord actionRefresh;
  466.     static ActionRecord soundFx;
  467.     Button *b;
  468.     ActionRecord **paction;
  469.     int n;
  470.  
  471.     actionRefresh.action = ActionRefresh;
  472.     actionRefresh.next = 0;
  473.     
  474.     soundFx.action = ActionPlaySound;
  475.     soundFx.next = &actionRefresh;
  476.  
  477.     b = (Button *)e->character;
  478.  
  479.     if (e->oldState != e->renderState) {
  480.         
  481.         paction = &actionRefresh.next;
  482.         
  483.         if (b->conditionList) {
  484.             *paction = b->getActionFromTransition(e->renderState, e->oldState);
  485.         } else if (e->renderState == stateDown) {
  486.             /* if the button is pressed and 
  487.                no condition list is defined*/
  488.             *paction = b->actionRecords;
  489.         }
  490.         
  491.         switch(e->renderState) {
  492.         case stateUp:
  493.             n = 0;
  494.             break;
  495.         case stateOver:
  496.             n = 1;
  497.             break;
  498.         default:
  499.             /* case stateDown: */
  500.             n = 2;
  501.             break;
  502.         }
  503.         
  504.         if (b->sound[n]) {
  505.             soundFx.sound = b->sound[n];
  506.             h->action = &soundFx;
  507.         } else {
  508.             h->action = &actionRefresh;
  509.         }
  510.         
  511.         e->oldState = e->renderState;
  512.  
  513.         h->prg = prg;
  514.         return 2;
  515.     }
  516.     h->action = 0;    // Nothing to do about this
  517.     return 0;
  518. }
  519.  
  520. int computeActions(FlashMovie *movie, Program **prg, ActionRecord **ar)
  521. {
  522.     ButtonAction h;
  523.  
  524.     h.action = NULL;
  525.     exploreButtons(movie, &h, button_action);
  526.     if (h.action) {
  527.         *prg = h.prg;
  528.     *ar = h.action;
  529.     return 1;
  530.     }
  531.     return 0;
  532. }
  533.  
  534. #define FOCUS_ZOOM       1.5
  535. /* in pixels */
  536. #define FOCUS_SIZE_MIN   50
  537. #define FOCUS_TRANSLATE  15
  538.  
  539. int
  540. DisplayList::render(GraphicDevice *gd, Matrix *render_matrix, Cxform *cxform)
  541. {
  542.     DisplayListEntry *e,*cur_focus;
  543.     int sprite = 0;
  544.     long n = 0;
  545.         Cxform cxf,*cxf1;
  546.     Rect bb,boundary;
  547.  
  548.         cur_focus = NULL;
  549.  
  550.     /*
  551.     if (isSprite == 0) {
  552.         if (this->bbox.xmin == LONG_MAX) return 0;
  553.         gd->updateClippingRegion(&this->bbox, render_matrix);
  554.         gd->clearCanvas();
  555.     }
  556.     */
  557.  
  558.     for (e = list; e; e = e->next)
  559.     {
  560. #if PRINT
  561.         printf("Character %3d @ %3d\n", e->character ? e->character->getTagId() : 0, e->depth);
  562. #endif
  563.         if (e->character) {
  564.             Matrix mat;
  565.  
  566.             if (render_matrix) {
  567.                 mat = *render_matrix;
  568.             }
  569.  
  570.             if (e->matrix) {
  571.                 mat = mat * (*e->matrix);
  572.             }
  573.  
  574.                         /* fast clipping */
  575.             // If object boundaries are outside current clip region give up with rendering
  576.                         e->character->getBoundingBox(&boundary,e);
  577.                         if (boundary.xmin != LONG_MAX) {
  578.                             Matrix tmat;
  579.  
  580.                             tmat = (*gd->adjust) * mat;
  581.                             transformBoundingBox(&bb, &tmat, &boundary, 1);
  582.  
  583.                             bb.xmin = bb.xmin >> FRAC_BITS;
  584.                             bb.ymin = bb.ymin >> FRAC_BITS;
  585.                             bb.xmax = (bb.xmax + FRAC - 1) >> FRAC_BITS;
  586.                             bb.ymax = (bb.ymax + FRAC - 1) >> FRAC_BITS;
  587.  
  588.                             if (bb.xmin >= gd->clip_rect.xmax ||
  589.                                 bb.xmax <= gd->clip_rect.xmin ||
  590.                                 bb.ymin >= gd->clip_rect.ymax ||
  591.                                 bb.ymax <= gd->clip_rect.ymin) {
  592.                                 continue;
  593.                             }
  594.                         }
  595.  
  596.                         if (cxform == NULL) {
  597.                             cxf1 = e->cxform;
  598.                         }
  599.             else if (e->cxform == NULL) {
  600.                             cxf1 = cxform;
  601.                         }
  602.             else {
  603.                             cxf1 = &cxf;
  604.                             cxf.ra = cxform->ra * e->cxform->ra;
  605.                             cxf.ga = cxform->ga * e->cxform->ga;
  606.                             cxf.ba = cxform->ba * e->cxform->ba;
  607.                             cxf.aa = cxform->aa * e->cxform->aa;
  608.                             
  609.                             cxf.rb = (long)(cxform->ra * e->cxform->rb + cxform->rb);
  610.                             cxf.gb = (long)(cxform->ga * e->cxform->gb + cxform->gb);
  611.                             cxf.bb = (long)(cxform->ba * e->cxform->bb + cxform->bb);
  612.                             cxf.ab = (long)(cxform->aa * e->cxform->ab + cxform->ab);
  613.                         }
  614.  
  615.                         if (e->character->isButton()) {
  616.                             Button *b = (Button *) e->character;
  617.  
  618.                             e->renderMatrix = mat;
  619.  
  620.                             if (e->renderState != stateUp && movie->mouse_active == 0) {
  621.                                 cur_focus = e;
  622.                 ((Button *)e->character)->updateButtonState(e);
  623.                             }
  624.  
  625.                             if (b->execute(gd, &mat, cxf1, e->renderState)) {
  626.                 sprite = 1;
  627.                             }
  628.                         } else {
  629.                             if (e->character->execute(gd, &mat, cxf1)) {
  630.                 sprite = 1;
  631.                             }
  632.                         }
  633.  
  634.             n++;
  635.         }
  636.     }
  637.  
  638. #if 0
  639.     {
  640.     /* display the bounding box (debug) */
  641.     Matrix tmat;
  642.     long x1,x2,y1,y2;
  643.     Color white;
  644.  
  645.     white.red = 255;
  646.     white.green = white.blue = 0;
  647.     gd->setForegroundColor(white);
  648.  
  649.     if (render_matrix) {
  650.         tmat = (*gd->adjust) * (*render_matrix);
  651.     } else {
  652.         tmat = *gd->adjust;
  653.     }
  654.     x1 = bbox.xmin;
  655.     y1 = bbox.ymin;
  656.     x2 = bbox.xmax;
  657.     y2 = bbox.ymax;
  658.     gd->drawLine(tmat.getX(x1,y1),tmat.getY(x1,y1),tmat.getX(x2,y1),tmat.getY(x2,y1),10*FRAC);
  659.     gd->drawLine(tmat.getX(x2,y1),tmat.getY(x2,y1),tmat.getX(x2,y2),tmat.getY(x2,y2),10*FRAC);
  660.     gd->drawLine(tmat.getX(x2,y2),tmat.getY(x2,y2),tmat.getX(x1,y2),tmat.getY(x1,y2),10*FRAC);
  661.     gd->drawLine(tmat.getX(x1,y2),tmat.getY(x1,y2),tmat.getX(x1,y1),tmat.getY(x1,y1),10*FRAC);
  662.     bbox.print();
  663.     }
  664. #endif
  665.         
  666.     // Reset clipping zone
  667.         bbox.reset();
  668.  
  669.     return sprite;
  670. }
  671.  
  672. void
  673. DisplayList::getBoundary(Rect *bb)
  674. {
  675.     DisplayListEntry *e;
  676.     Rect boundary;
  677.  
  678.     bb->reset();
  679.     for (e = list; e; e = e->next)
  680.     {
  681.         if (e->character) {
  682.             e->character->getBoundingBox(&boundary,e);
  683.             transformBoundingBox(bb, e->matrix, &boundary, 0);
  684.         }
  685.     }
  686. }
  687.  
  688. extern "C" {
  689.  
  690. void dump_buttons(FlashHandle flashHandle)
  691. {
  692. #if 0
  693.     Rect rect;
  694.     DisplayListEntry *e;
  695.     FlashMovie *movie;
  696.  
  697.     movie = (FlashMovie *)flashHandle;
  698.  
  699.     for (e = movie->first_button; e; e = e->next_button) {
  700.         computeBBox(movie,&rect,e);
  701.         printf("button: id=%d pos=%d %d %d %d\n",
  702.                e->character->getTagId(),
  703.                rect.xmin, rect.ymin, rect.xmax, rect.ymax);
  704.     }
  705. #endif
  706. }
  707.  
  708. }
  709.